package matchs

import (
	"context"
	"fmt"
	"gorm.io/gorm"
	"time"

	"github.com/flipped-aurora/gin-vue-admin/server/global"
	"github.com/flipped-aurora/gin-vue-admin/server/model/matchs"
	"go.uber.org/zap"
)

// AskMatchTask 问问匹配定时任务
type AskMatchTask struct{}

// NewAskMatchTask 创建问问匹配定时任务实例
func NewAskMatchTask() *AskMatchTask {
	return &AskMatchTask{}
}

// Execute 执行问问匹配定时任务
func (t *AskMatchTask) Execute(ctx context.Context) error {
	global.GVA_LOG.Info("问问匹配定时任务开始执行")
	startTime := time.Now()

	var totalTaskCount, processedCount, successCount, failCount int
	var batchSize = 50 // 每次处理的任务数量
	var page = 1
	var pageSize = batchSize

	for {
		// 分页查询符合条件的任务
		tasks, count, err := t.getTasksForMatch(ctx, page, pageSize)
		if err != nil {
			global.GVA_LOG.Error("查询待匹配任务失败", zap.Error(err))
			return fmt.Errorf("查询待匹配任务失败: %w", err)
		}

		if page == 1 {
			totalTaskCount = int(count)
			global.GVA_LOG.Info("查询到待处理任务总数", zap.Int("totalTaskCount", totalTaskCount))
		}

		// 处理当前页的任务
		if len(tasks) > 0 {
			processed, success, fail, err := t.processTasks(ctx, tasks)
			if err != nil {
				global.GVA_LOG.Error("处理任务批失败", zap.Error(err))
				// 继续处理下一批，不中断整个任务
			}
			processedCount += processed
			successCount += success
			failCount += fail
		} else {
			// 没有更多任务，退出循环
			break
		}

		// 准备下一页
		page++

		// 避免内存溢出，每处理1000个任务休息1秒
		if processedCount%1000 == 0 && processedCount > 0 {
			time.Sleep(1 * time.Second)
		}
	}

	// 更新索引（如果需要）
	t.ensureIndexExists()

	endTime := time.Now()
	duration := endTime.Sub(startTime)

	global.GVA_LOG.Info("问问匹配定时任务执行完成",
		zap.Int("totalTaskCount", totalTaskCount),
		zap.Int("processedCount", processedCount),
		zap.Int("successCount", successCount),
		zap.Int("failCount", failCount),
		zap.Duration("duration", duration),
	)

	return nil
}

// getTasksForMatch 查询待匹配的任务
func (t *AskMatchTask) getTasksForMatch(ctx context.Context, page, pageSize int) ([]matchs.MatchAskTask, int64, error) {
	var tasks []matchs.MatchAskTask
	var count int64

	db := global.GVA_DB.WithContext(ctx)

	// 构建查询条件
	query := db.Model(&matchs.MatchAskTask{}).
		Where("status = ?", matchs.MatchAskStatusMatching).
		Where("expire_at > ?", time.Now().In(global.LocTime)).
		Where("pushed_count < ?", 30)

	// 计算总数
	if err := query.Count(&count).Error; err != nil {
		return nil, 0, err
	}

	// 分页查询
	offset := (page - 1) * pageSize
	if err := query.Order("created_at DESC").Limit(pageSize).Offset(offset).Find(&tasks).Error; err != nil {
		return nil, 0, err
	}

	return tasks, count, nil
}

// processTasks 处理一批任务
func (t *AskMatchTask) processTasks(ctx context.Context, tasks []matchs.MatchAskTask) (processed, success, fail int, err error) {
	processed = len(tasks)

	for _, task := range tasks {
		// 执行匹配，最多重试3次
		retryCount := 0
		var pushedCount int
		var matchErr error

		for retryCount < 3 {
			pushedCount, matchErr = t.executeMatchWithRetry(ctx, uint(task.ID))
			if matchErr == nil {
				break
			}
			retryCount++
			global.GVA_LOG.Error("匹配任务失败，准备重试",
				zap.Uint("taskId", uint(task.ID)),
				zap.Int("retryCount", retryCount),
				zap.Error(matchErr))
			// 指数退避策略
			time.Sleep(time.Duration(retryCount*100) * time.Millisecond)
		}

		// 记录结果
		if matchErr == nil {
			success++
			global.GVA_LOG.Info("任务匹配成功",
				zap.Uint("taskId", uint(task.ID)),
				zap.Int("pushedCount", pushedCount),
				zap.Int("totalPushedCount", task.PushedCount+pushedCount))
		} else {
			fail++
			global.GVA_LOG.Error("任务匹配失败，已达到最大重试次数",
				zap.Uint("taskId", uint(task.ID)),
				zap.Int("retryCount", retryCount),
				zap.Error(matchErr))
			// 记录失败但继续处理其他任务
		}
	}

	return processed, success, fail, nil
}

// executeMatchWithRetry 执行匹配并处理重试
func (t *AskMatchTask) executeMatchWithRetry(ctx context.Context, taskID uint) (int, error) {
	// 使用全局唯一的锁避免并发问题
	lockKey := fmt.Sprintf("ask_match_lock:%d", taskID)
	result, err := global.GVA_Concurrency_Control_V2.Do(ctx, lockKey, func() (interface{}, error) {
		// 在事务中执行匹配和状态更新
		var pushedCount int
		err := global.GVA_DB.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
			// 再次检查任务状态（防止并发修改）
			var task matchs.MatchAskTask
			if err := tx.First(&task, taskID).Error; err != nil {
				return fmt.Errorf("查询任务失败: %w", err)
			}

			// 检查任务是否仍然符合匹配条件
			if task.Status != matchs.MatchAskStatusMatching ||
				time.Now().In(global.LocTime).After(task.ExpireAt) ||
				task.PushedCount >= 30 {
				return fmt.Errorf("任务已不符合匹配条件，当前状态: %d, 已推送: %d", task.Status, task.PushedCount)
			}

			// 执行匹配 - 传递事务对象，避免嵌套事务
			matchService := AskMatchServiceApp
			var err error
			pushedCount, err = matchService.DoAskMatch(ctx, taskID, 5, tx) // batchSize=5，传入事务对象tx
			if err != nil {
				return fmt.Errorf("执行匹配失败: %w", err)
			}

			// 注意：不再需要调用checkAndUpdateTaskStatus，因为DoAskMatch内部已处理状态更新
			// 保持事务一致性，避免重复更新

			return nil
		})

		return pushedCount, err
	})

	if err != nil {
		return 0, err
	}

	return result.(int), nil
}

// checkAndUpdateTaskStatusWithDB 使用指定数据库连接检查并更新任务状态
func (t *AskMatchTask) checkAndUpdateTaskStatusWithDB(ctx context.Context, tx *gorm.DB, taskID uint, newPushedCount int) error {
	// 查询任务最新状态
	var task matchs.MatchAskTask
	if err := tx.WithContext(ctx).First(&task, taskID).Error; err != nil {
		return err
	}

	// 检查是否推满30人
	if task.PushedCount+newPushedCount >= 30 {
		return tx.WithContext(ctx).Model(&task).Updates(map[string]interface{}{
			"status":       matchs.MatchAskStatusReplied,
			"completed_at": time.Now().In(global.LocTime),
		}).Error
	}

	// 检查是否有回复
	var replyCount int64
	if err := tx.WithContext(ctx).Model(&matchs.MatchAskReply{}).Where("task_id = ?", taskID).Count(&replyCount).Error; err != nil {
		return err
	}

	if replyCount > 0 {
		return tx.WithContext(ctx).Model(&task).Updates(map[string]interface{}{
			"status":       matchs.MatchAskStatusReplied,
			"completed_at": time.Now().In(global.LocTime),
		}).Error
	}

	return nil
}

// checkAndUpdateTaskStatus 检查并更新任务状态（保持向后兼容）
func (t *AskMatchTask) checkAndUpdateTaskStatus(tx *gorm.DB, taskID uint, newPushedCount int) error {
	return t.checkAndUpdateTaskStatusWithDB(context.Background(), tx, taskID, newPushedCount)
}

// ensureIndexExists 确保索引存在
func (t *AskMatchTask) ensureIndexExists() {
	// 为MatchAskTask表添加复合索引，优化查询性能
	db := global.GVA_DB

	// 使用更兼容的方式创建索引，适用于不支持IF NOT EXISTS语法的MySQL版本
	// 1. 创建match_ask_task表的复合索引
	var indexExists int
	db.Raw(`SELECT COUNT(*) INTO @index_exists FROM information_schema.statistics 
		WHERE table_schema = DATABASE() AND table_name = 'match_ask_task' AND index_name = 'idx_match_ask_task_match'`)
	db.Raw("SELECT @index_exists").Scan(&indexExists)
	if indexExists == 0 {
		if err := db.Exec("CREATE INDEX idx_match_ask_task_match ON match_ask_task(status, expire_at, pushed_count)").Error; err != nil {
			global.GVA_LOG.Warn("创建match_ask_task表索引失败", zap.Error(err))
		} else {
			global.GVA_LOG.Info("成功创建match_ask_task表索引")
		}
	}

	// 2. 创建match_ask_reply表的索引
	db.Raw(`SELECT COUNT(*) INTO @index_exists FROM information_schema.statistics 
		WHERE table_schema = DATABASE() AND table_name = 'match_ask_reply' AND index_name = 'idx_match_ask_reply_task_id'`)
	db.Raw("SELECT @index_exists").Scan(&indexExists)
	if indexExists == 0 {
		if err := db.Exec("CREATE INDEX idx_match_ask_reply_task_id ON match_ask_reply(task_id)").Error; err != nil {
			global.GVA_LOG.Warn("创建match_ask_reply表索引失败", zap.Error(err))
		} else {
			global.GVA_LOG.Info("成功创建match_ask_reply表索引")
		}
	}

	global.GVA_LOG.Info("索引检查完成")
}
